/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package lmge.panamws;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;

/**
 *
 * @author dttung
 */
@WebService(serviceName = "panamws")
public class panamws {
    final String panamdir = "/users_local1/tung/gridpanam"; // base directory of panam
    final String workflowsdir = panamdir + "/workflows"; // Stored local workflow files
    final String qualitydir = panamdir + "/quality"; // Stored quality files
    final String preprocessdir = panamdir + "/preprocess"; // Stored preprocess files
    final String alnphydir = panamdir + "/alnphy"; // Stored alnphy files
    final String finaldir = panamdir + "/final"; // Stored final files
    final String panaminidir = panamdir + "/panaminis"; // Stored inis files

    final String gridpanamdir = "/biomed/user/d/dttung/panam"; // base directory of panam on the grid
    final String gridpanamdirinputs  = gridpanamdir + "/inputs";
    final String gridpanamdirtools   = gridpanamdir + "/tools";
    final String gridpanamdiroutputs = gridpanamdir + "/outputs";
    final String diracdir = "/home/tung/diracv6r7p15/scripts"; // dirac commands
    final int nClone = 5; // Number of clone jobs for each panam job
    /**
     * Submit a quality job on the grid.
     * @param workflow is the LFN of the workflow directory where all the intputs, output are stored
     * @return the DIRAC's job id.
     */
    @WebMethod(operationName = "submitQualityJob")
    public  ArrayList<String> submitQualityJob(@WebParam(name = "workflow") String workflow) {
        ArrayList<String> jobids = new ArrayList<String>();
        
        // Generate JDL file for quality job
        for (int clone = 1; clone <= nClone; clone++)
        {
            try
            {
                generateQualityJDL(workflow, clone);
            } catch (IOException ioe)
            {
                //String jobid = "Error: " + ioe.getMessage();
                return null; // actually erro msg, not jobid
            }
        
            // Submit quality job with generated JDL file
            String[] cmd = {diracdir + "/dirac-wms-job-submit", workflowsdir +"/" + workflow + "/quality-" + clone + ".jdl"};
            String jobid = executeShellScript(cmd);
            jobids.add(jobid);
        }
        return jobids;
    }
    private void generateQualityJDL(String workflow, int clone) throws IOException
    {
        // Create directory workflow if not exist
        File workflowdir = new File(workflowsdir + "/" + workflow);

        // if the directory does not exist, create it
        if (!workflowdir.exists()) {
            boolean result = workflowdir.mkdir(); 
            if(!result) {
                throw new IOException("Error creating directory " + workflowdir);
           }
        }
        // The name of the file to open.
        String fileName = workflowsdir + "/" + workflow + "/quality-" + clone + ".jdl";

        try {
            // Assume default encoding.
            FileWriter fileWriter = new FileWriter(fileName);

            // Always wrap FileWriter in BufferedWriter.
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

            // Note that write() does not automatically
            // append a newline character.
            bufferedWriter.write("JobName = \"quality-" + clone + "-" + workflow +"\";");
            bufferedWriter.newLine();
            bufferedWriter.write("Executable = \"quality.sh\";");
            bufferedWriter.newLine();
            bufferedWriter.write("Arguments = \""+workflow+"\";");
            bufferedWriter.newLine();
            bufferedWriter.write("StdOutput = \"StdOut\";");
            bufferedWriter.newLine();
            bufferedWriter.write("StdError = \"StdErr\";");
            bufferedWriter.newLine();
            // At the moment panam.ini is stored in panamdir for simple testing
            // but it should be created and stored in workflowsdir when integreting with portal
            String inputsandbox = "InputSandbox = {\""+qualitydir+"/quality.sh\", \"LFN:";
            inputsandbox += gridpanamdirtools+"/qualitytool.tar.gz\",\"LFN:";
            inputsandbox += gridpanamdirinputs+"/"+workflow+".tar.gz\",\"";
            inputsandbox += workflowsdir+"/" + workflow + "/panam.ini\",\""+qualitydir+"/quality.pl\",\"";
            inputsandbox += panamdir+"/pnls.sh\",\"";
            inputsandbox += panamdir+"/goodses.txt\",\"" + panamdir+"/sefunctions.sh\"};";
            bufferedWriter.write(inputsandbox);
            bufferedWriter.newLine();
            bufferedWriter.write("OutputSandbox = {\"StdOut\",\"StdErr\",\"compileemboss.log\",\"copyquality.log\"};");
            bufferedWriter.newLine();

            // Always close files.
            bufferedWriter.close();
        }
        catch(IOException ex) {
            // throw an exception
            throw new IOException("Error writing to file '" + fileName + "'");
        }
    }
        
    private void generateAlnPhyJDL(String workflow, String taxogroup, int clone) throws IOException
    {
        // The name of the file to open.
        String fileName = workflowsdir + "/" + workflow + "/alnphy-" + taxogroup + "-" + clone + ".jdl";
        try {
            // Assume default encoding.
            FileWriter fileWriter = new FileWriter(fileName);

            // Always wrap FileWriter in BufferedWriter.
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

            // Note that write() does not automatically
            // append a newline character.
            bufferedWriter.write("JobName = \"alnphy-" + workflow + "-" + taxogroup + "-" + clone + "\";");
            bufferedWriter.newLine();        
            bufferedWriter.write("Executable = \"alnphy.sh\";");
            bufferedWriter.newLine();
            bufferedWriter.write("Arguments = \"" + workflow+ " " + taxogroup + "\";");
            bufferedWriter.newLine();
            bufferedWriter.write("StdOutput = \"StdOut\";");
            bufferedWriter.newLine();
            bufferedWriter.write("StdError = \"StdErr\";");
            bufferedWriter.newLine();
            bufferedWriter.write("InputSandbox = {\""+alnphydir+"/alnphy.sh\","
                    + "\""+alnphydir+"/align.pl\","
                    + "\""+alnphydir+"/fasttree.pl\","
                    + "\""+alnphydir+"/nodes.pl\","
                    + "\""+alnphydir+"/parsing.pl\","
                    + "\""+alnphydir+"/rooting.pl\","
                    + "\""+alnphydir+"/clade.pl\","
                    
                    + "\"LFN:"+gridpanamdirtools+"/alnphytool.tar.gz\","
                    + "\"LFN:"+gridpanamdiroutputs+"/"+workflow+"/preprocess.tar.gz\","                    
                    + "\"LFN:"+gridpanamdirinputs+"/Reference-" + workflow + ".tar.gz\","
                    
                    + "\""+workflowsdir+"/" + workflow + "/panam.ini\","                    
                    + "\""+panamdir+"/goodses.txt\","
                    + "\""+panamdir+"/pnls.sh\","
                    + "\""+panamdir+"/sefunctions.sh\"};");
            bufferedWriter.newLine();
            bufferedWriter.write("OutputSandbox = {\"StdOut\",\"StdErr\",\"copyalnphy.log\"};");
            bufferedWriter.newLine();

            // Always close files.
            bufferedWriter.close();
        }
        catch(IOException ex) {
            // throw an exception
            throw new IOException("Error writing to file '" + fileName + "'");
        }
    }
    /**
     * Get a state of a generic job.
     * @param jobId is the id of the job returned by a 'submit' services.
     * @return the state of the job. Job states are: received, waiting, matched, running, done, completed, stalled, failed.
     */
    @WebMethod(operationName = "getJobState")
    public String getJobState(@WebParam(name = "jobId") String jobId) {
        String jobstate = "hello";
        if (jobId.toLowerCase().startsWith("jobid")) // job submission is successful
        {
            String jid = jobId.substring(8, jobId.length());
            String[] cmd = {panamdir + "/getjobstate.sh",jid}; // To call getjobstate.sh with jid as args
            jobstate = executeShellScript(cmd);

            if (! jobstate.isEmpty()) return jobstate.toLowerCase();
            else return "failed";
        }
        else return "failed";
    }

    /**
     * Check the return of quality job.
     * @param qualityJobId is the id of the quality job returned by submitQualityJob.
     * @param workflowdir is the LFN of the workflow directory where all the intputs, output are stored
     * @return true only if job state is completed and a LFN file of quality job output is existed.
     */
    @WebMethod(operationName = "checkQualityJobReturn")
    public String checkQualityJobReturn(@WebParam(name = "qualityJobIds") ArrayList<String> qualityJobIds,
                                         @WebParam(name = "workflowdir") String workflowdir) {
        String qualitystate = "false";
        //for (int i = 0; i < qualityJobIds.size(); i++)
        //{
        //    String qualityJobId = qualityJobIds.get(i);
        //    if (qualityJobId.toLowerCase().startsWith("jobid")) // job submission is successful
        //    {
        //        String jid = qualityJobId.substring(8, qualityJobId.length());
                String[] cmd = {qualitydir + "/checkquality.sh", "jid", workflowdir};
                qualitystate = executeShellScript(cmd);
                if (qualitystate.equals("true")) return "true";
        //    }
        //}
        return qualitystate;
    }

    /**
     * Submit 'preprocess' job on the grid.
     * @param panamini is the configuration file for this job
     * @param workflowdir is the LFN of the workflow directory where all the intputs, output are stored
     * @return DIRAC's job id
     */
    @WebMethod(operationName = "submitPreprocessJob")
    public ArrayList<String> submitPreprocessJob(@WebParam(name = "workflow") String workflow) {
        ArrayList<String> jobids = new ArrayList<String>();
        
        for (int clone = 1; clone <= nClone; clone ++)
        {
            // Generate JDL file for quality job
            try
            {
                generatePreprocessJDL(workflow, clone);
            } catch (IOException ioe)
            {
            }
            // Submit quality job with generated JDL file
            String[] cmd = {diracdir + "/dirac-wms-job-submit", workflowsdir +"/" + workflow + "/preprocess-" + clone + ".jdl"};
            String jobid = executeShellScript(cmd);
            jobids.add(jobid);
        }
        return jobids;
    }
    private void generatePreprocessJDL(String workflow, int clone) throws IOException
    {
        // The name of the file to open.
        String fileName = workflowsdir + "/" + workflow + "/preprocess-"+clone+".jdl";
        try {
            // Assume default encoding.
            FileWriter fileWriter = new FileWriter(fileName);

            // Always wrap FileWriter in BufferedWriter.
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

            // Note that write() does not automatically
            // append a newline character.
            bufferedWriter.write("JobName = \"preprocess-" +clone+"-" + workflow +"\";");
            bufferedWriter.newLine();        
            bufferedWriter.write("Executable = \"preprocess.sh\";");
            bufferedWriter.newLine();
            bufferedWriter.write("Arguments = \"" + workflow+"\";");
            bufferedWriter.newLine();
            bufferedWriter.write("StdOutput = \"StdOut\";");
            bufferedWriter.newLine();
            bufferedWriter.write("StdError = \"StdErr\";");
            bufferedWriter.newLine();
            bufferedWriter.write("InputSandbox = {\""+preprocessdir+"/preprocess.sh\","
                    + "\""+preprocessdir+"/trimsortsplit.sh\","
                    + "\""+preprocessdir+"/gentaxogroup.sh\","
                    + "\""+preprocessdir+"/trimming.pl\","
                    + "\""+preprocessdir+"/preprocess.pl\","
                    + "\""+preprocessdir+"/cutting.pl\","
                    + "\""+preprocessdir+"/sorting.pl\","
                    + "\"LFN:"+gridpanamdirtools+"/preprocesstool.tar.gz\","
                    + "\"LFN:"+gridpanamdiroutputs+"/"+workflow+"/quality.tar.gz\","
                    //+ "\"LFN:"+gridpanamdirinputs+"/"+workflow+"-pre.tar.gz\","
                    + "\"LFN:"+gridpanamdirinputs+"/Reference-" + workflow +".tar.gz\","
                    + "\""+workflowsdir + "/" + workflow + "/panam.ini\","          // chu y o day!!!!          
                    + "\""+panamdir+"/goodses.txt\","
                    + "\""+panamdir+"/pnls.sh\","
                    + "\""+panamdir+"/sefunctions.sh\"};");
            bufferedWriter.newLine();
            bufferedWriter.write("OutputSandbox = {\"StdOut\",\"StdErr\",\"copypreprocess.log\"};");
            bufferedWriter.newLine();

            // Always close files.
            bufferedWriter.close();
        }
        catch(IOException ex) {
            // throw an exception
            throw new IOException("Error writing to file '" + fileName + "'");
        }
    }
    /**
     * Check the return of the preprocess job.
     * @param preprocessJobId is the job id submitted by submitPreprocessJob service.
     * @param workflowdir is the LFN of the workflow directory where all the intputs, output are stored
     * @return true only if job state is completed and a LFN file of preprocess job output is existed. 
     */
    @WebMethod(operationName = "checkPreprocessJobReturn")
    public String checkPreprocessJobReturn(@WebParam(name = "preprocessJobId") ArrayList<String> preprocessJobIds,
                                            @WebParam(name = "workflowdir") String workflowdir) {
        String preprocessstate = "false";
        //for (int i = 0; i < preprocessJobIds.size(); i++)
        //{
        //    String preprocessJobId = preprocessJobIds.get(i);
        //    if (preprocessJobId.toLowerCase().startsWith("jobid")) // job submission is successful
        //    {
        //        String jid = preprocessJobId.substring(8, preprocessJobId.length());
                String[] cmd = {preprocessdir + "/checkpreprocess.sh", "jid", workflowdir};
                preprocessstate = executeShellScript(cmd);
                if (preprocessstate.equals("true")) return "true";
        //    }
        //}
        return preprocessstate;
    }

    /**
     * Get list of taxo groups after the preprocess service.
     * @param workflowdir The workflow directory
     * @return List of taxo groups
     */
    @WebMethod(operationName = "getTaxoGroupsAfterPreprocess")
    public ArrayList<String> getTaxoGroupsAfterPreprocess(@WebParam(name = "workflowdir") String workflowdir) {
        ArrayList<String> taxogroups = new ArrayList<String>();
        String[] cmd = {panamdir + "/gettaxogroup.sh", workflowdir};
        
        // download taxogroups.txt
        executeShellScript(cmd);
        // read content of taxogroups.txt
        try {
            BufferedReader br = new BufferedReader(new FileReader(panamdir + "/workflows/" + workflowdir + "/taxogroups.txt"));
            String line;
        
            while ((line = br.readLine()) != null) {
               taxogroups.add(line);
            }
            br.close();
        } catch (IOException ex) {
            taxogroups.add("Error: " + ex.getMessage());
        }
        
        return taxogroups;
    }

    /**
     * Submit AlnPhy service, including: Alignment, FastTree, Rooting, Noding and Parsing.
     * @param paramini is the configuration file for this job
     * @param workflowdir is the LFN of the workflow directory where all the intputs, output are stored
     * @param taxogroup is one of the taxo groups id returned from preprocess
     * @return DIRAC job id
     */
    @WebMethod(operationName = "submitAlnPhyJob")
    public ArrayList<String> submitAlnPhyJob(@WebParam(name = "workflow") String workflow, 
                               @WebParam(name = "taxogroup") String taxogroup) {
        ArrayList<String> jobids = new ArrayList<String>();
        for (int clone = 1; clone <= nClone; clone++)
        // Generate JDL file for quality job
        {
            try
            {
                generateAlnPhyJDL(workflow, taxogroup, clone);
            } catch (IOException ioe)
            {
            }
            // Submit quality job with generated JDL file
            String[] cmd = {diracdir + "/dirac-wms-job-submit", workflowsdir +"/" + workflow + "/alnphy-" + taxogroup + "-" + clone + ".jdl"};
            String jobid = executeShellScript(cmd);
            jobids.add(jobid);
        }
        return jobids;
    }

    /**
     * Check the return of the AlnPhy job.
     * @param jobId is the DIRAC job id returned by submitAlnPhyJob.
     * @return true if the job state is completed and a LFN of AlnPhyJob output exists.
     */
    @WebMethod(operationName = "checkAlnPhyJobReturn")
    public String checkAlnPhyJobReturn(@WebParam(name = "alnphyJobId") ArrayList<String> alnphyJobIds,
                                            @WebParam(name = "workflowdir") String workflowdir,
                                            @WebParam(name = "taxogroup") String taxogroup) 
    {
        String alnphystate = "false";
        //for (int i = 0; i < alnphyJobIds.size(); i++)
        //{
        //    String alnphyJobId = alnphyJobIds.get(i);
        //    if (alnphyJobId.toLowerCase().startsWith("jobid")) // job submission is successful
        //    {
        //        String jid = alnphyJobId.substring(8, alnphyJobId.length());
        String[] cmd = {alnphydir + "/checkalnphy.sh", "jid", workflowdir,taxogroup};
        alnphystate = executeShellScript(cmd);
        if (alnphystate.equals("true")) return "true";
                
        //    }
        //}
        return alnphystate;
    }
    /**
     * Submit final service.
     * @param panamini the configuration file of panam.
     * @param workflowdir the LFN of workflow directory on the grid.
     * @return the DIRAC's job id.
     */
    @WebMethod(operationName = "submitFinalJob")
    public ArrayList<String> submitFinalJob(@WebParam(name = "workflowdir") String workflowdir) 
    {        
        ArrayList<String> jobids = new ArrayList<String>();
        for (int clone = 1; clone <= nClone; clone++)
        {
            // Generate JDL file for final job
            try
            {
                generateFinalJDL(workflowdir, clone);
            } catch (IOException ioe)
            {
            }
            // Submit quality job with generated JDL file
            String[] cmd = {diracdir + "/dirac-wms-job-submit", workflowsdir +"/" + workflowdir + "/final-" + clone + ".jdl"};
            String jobid = executeShellScript(cmd);
            jobids.add(jobid);
        }
        return jobids;
    }
    private void generateFinalJDL(String workflow, int clone) throws IOException
    {
        // The name of the file to open.
        String fileName = workflowsdir + "/" + workflow + "/final-" + clone + ".jdl";
        try {
            // Assume default encoding.
            FileWriter fileWriter = new FileWriter(fileName);

            // Always wrap FileWriter in BufferedWriter.
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

            // Note that write() does not automatically
            // append a newline character.
            bufferedWriter.write("JobName = \"final-" + workflow + "-" + clone + "\";");
            bufferedWriter.newLine();        
            bufferedWriter.write("Executable = \"final.sh\";");
            bufferedWriter.newLine();
            bufferedWriter.write("Arguments = \"" + workflow + "\";");
            bufferedWriter.newLine();
            bufferedWriter.write("StdOutput = \"StdOut\";");
            bufferedWriter.newLine();
            bufferedWriter.write("StdError = \"StdErr\";");
            bufferedWriter.newLine();
            bufferedWriter.write("InputSandbox = {\""+finaldir+"/final.sh\","
                    + "\""+finaldir+"/final.pl\","
                    + "\""+panamdir+"/pnls.sh\","
                                        
                    + "\"LFN:"+gridpanamdiroutputs+"/"+workflow+"/preprocess.tar.gz\","                    
                    // new version: no need $workflow-pre.tar.gz
                    //+ "\"LFN:"+gridpanamdirinputs+"/"+workflow+"-pre.tar.gz\","
                    
                    + "\""+workflowsdir + "/" + workflow +"/panam.ini\","                    
                    + "\""+panamdir+"/goodses.txt\","
                    + "\""+panamdir+"/sefunctions.sh\"};");
            bufferedWriter.newLine();
            bufferedWriter.write("OutputSandbox = {\"StdOut\",\"StdErr\",\"copyfinal.log\"};");
            bufferedWriter.newLine();

            // Always close files.
            bufferedWriter.close();
        }
        catch(IOException ex) {
            // throw an exception
            throw new IOException("Error writing to file '" + fileName + "'");
        }
    }

    /**
     * Check the return of the Clade job.
     * @param jobId is the DIRAC job id returned by submitCladeJob.
     * @return true if the job state is completed and a LFN of Clade service output exists.
     */
    @WebMethod(operationName = "checkFinalJobReturn")
    public String checkFinalJobReturn(@WebParam(name = "jobId") ArrayList<String> jobIds, @WebParam(name = "workflowdir") String workflowdir) {
        String finalstate = "false";
        //for (int i = 0; i < jobIds.size(); i++)
        //{
        //    String jobId = jobIds.get(i);
        //    if (jobId.toLowerCase().startsWith("jobid")) // job submission is successful
        //   {
        //        String jid = jobId.substring(8, jobId.length());
                String[] cmd = {finaldir + "/checkfinal.sh", "jid", workflowdir};
                finalstate = executeShellScript(cmd);
                if (finalstate.equals("true")) return "true";
        //    }
        //}
        return finalstate;
    }

    private String executeShellScript(String[] cmd)
    {
        String output = "";
        try
        {
            Runtime env = Runtime.getRuntime();
            //String[] cmd = {panamdir + "/getjobstate.sh",jid}; // To call getjobstate.sh with jid as args
            Process process = env.exec(cmd);
            InputStreamReader isr =  new InputStreamReader ( process.getInputStream() );
            BufferedReader stdout = new BufferedReader ( isr );

            while((output = stdout.readLine()) != null )
            {
                if ( ! output.isEmpty() ) break;
            }
            process.waitFor();

            process.getInputStream().close();
            stdout.close();
            isr.close();
            process.destroy();
        }
        catch( Exception e)
        {
            output = "Error: " + e.getMessage();
        }
        return output;
    }
}
